#include <windows.h>
#include <shlobj.h>
#include <string>
#include <vector>
#include <algorithm>
#include <objbase.h>
#include <shlwapi.h>
#include <comdef.h>
#include <sstream>
#define CLASS_E_CLASSNOTFOUND _HRESULT_TYPEDEF_(0x80040111L)

#define IDM_DISPLAY_PATHS 0




// 全局变量
HINSTANCE g_hInst = NULL;
const CLSID CLSID_PdfContextMenu = {0x1D4988A1, 0xB9A5, 0x4E5D, {0x8A, 0x3C, 0x3A, 0x5E, 0x9D, 0x1F, 0x3B, 0x7A}};

class CPdfContextMenu : public IShellExtInit, public IContextMenu
{
private:
    LONG m_cRef;
    std::vector<std::wstring> m_files;

public:
    CPdfContextMenu() : m_cRef(1) {}

    // IUnknown methods
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        if (riid == IID_IUnknown || riid == IID_IShellExtInit)
        {
            *ppv = static_cast<IShellExtInit *>(this);
        }
        else if (riid == IID_IContextMenu)
        {
            *ppv = static_cast<IContextMenu *>(this);
        }
        else
        {
            *ppv = NULL;
            return E_NOINTERFACE;
        }
        AddRef();
        return S_OK;
    }

    STDMETHODIMP_(ULONG)
    AddRef()
    {
        return InterlockedIncrement(&m_cRef);
    }

    STDMETHODIMP_(ULONG)
    Release()
    {
        ULONG cRef = InterlockedDecrement(&m_cRef);
        if (cRef == 0)
        {
            delete this;
        }
        return cRef;
    }

    // IShellExtInit method
    STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hkeyProgID)
    {
        if (pDataObj == NULL)
            return E_INVALIDARG;

        FORMATETC fmt = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
        STGMEDIUM stg = {TYMED_HGLOBAL};
        HDROP hDrop;

        if (FAILED(pDataObj->GetData(&fmt, &stg)))
            return E_FAIL;

        hDrop = (HDROP)GlobalLock(stg.hGlobal);
        if (hDrop == NULL)
            return E_FAIL;

        // 获取文件数量
        UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
        m_files.clear();

        // 检查所有文件是否都是PDF
        bool allPdf = true;
        for (UINT i = 0; i < nFiles; i++)
        {
            wchar_t szFile[MAX_PATH];
            DragQueryFile(hDrop, i, szFile, MAX_PATH);

            // 检查文件扩展名
            std::wstring file(szFile);
            size_t dotPos = file.find_last_of(L".");
            if (dotPos == std::wstring::npos ||
                _wcsicmp(file.substr(dotPos).c_str(), L".pdf") != 0)
            {
                allPdf = false;
                break;
            }

            m_files.push_back(file);
        }

        GlobalUnlock(stg.hGlobal);
        ReleaseStgMedium(&stg);

        // 如果不是所有文件都是PDF，清空列表
        if (!allPdf)
        {
            m_files.clear();
        }

        return allPdf ? S_OK : E_FAIL;
    }

    // IContextMenu methods
    STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
    {
        if ((uFlags & CMF_DEFAULTONLY) || m_files.empty())
            return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);

        InsertMenu(hmenu, indexMenu, MF_BYPOSITION | MF_STRING,
                   idCmdFirst + IDM_DISPLAY_PATHS, L"打印所选PDF文件");

        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
    }

    STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici)
    {
        if (HIWORD(pici->lpVerb) != 0)
            return E_FAIL;

        if (LOWORD(pici->lpVerb) != IDM_DISPLAY_PATHS)
            return E_FAIL;

      

        auto res = MessageBox(pici->hwnd, L"确定要打印所选PDF文件", L"PDF打印", MB_OKCANCEL);

        OutputDebugStringA(std::to_string(res).c_str());
        // 发送打印请求 outputdebugstringw 并拷贝文件至临时文件夹
        for(auto file :m_files){

            OutputDebugStringW(file.c_str());
        }
      

        return S_OK;
    }

    STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pReserved, LPSTR pszName, UINT cchMax)
    {
        if (idCmd != IDM_DISPLAY_PATHS)
            return E_INVALIDARG;

        if (uType == GCS_HELPTEXT)
        {
            if (pszName)
            {
                std::wstring helpText = L"显示选中的所有PDF文件的完整路径";
                lstrcpynW((LPWSTR)pszName, helpText.c_str(), cchMax);
            }
            return S_OK;
        }

        return E_NOTIMPL;
    }
};

// Class factory
class CPdfContextMenuClassFactory : public IClassFactory
{
public:
    // IUnknown
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        if (riid == IID_IUnknown || riid == IID_IClassFactory)
        {
            *ppv = static_cast<IClassFactory *>(this);
            return S_OK;
        }
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    STDMETHODIMP_(ULONG)
    AddRef()
    {
        return 2;
    }

    STDMETHODIMP_(ULONG)
    Release()
    {
        return 1;
    }

    // IClassFactory
    STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
    {
        if (pUnkOuter != NULL)
            return CLASS_E_NOAGGREGATION;

        CPdfContextMenu *pPdfContextMenu = new (std::nothrow) CPdfContextMenu();
        if (pPdfContextMenu == NULL)
            return E_OUTOFMEMORY;

        HRESULT hr = pPdfContextMenu->QueryInterface(riid, ppv);
        pPdfContextMenu->Release();
        return hr;
    }

    STDMETHODIMP LockServer(BOOL fLock)
    {
        return S_OK;
    }
};

// DLL exports
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
    if (!IsEqualCLSID(rclsid, CLSID_PdfContextMenu))
        return CLASS_E_CLASSNOTFOUND;

    CPdfContextMenuClassFactory *pcf = new (std::nothrow) CPdfContextMenuClassFactory();
    if (pcf == NULL)
        return E_OUTOFMEMORY;

    HRESULT hr = pcf->QueryInterface(riid, ppv);
    pcf->Release();
    return hr;
}

STDAPI DllCanUnloadNow()
{
    return S_OK;
}

STDAPI DllRegisterServer()
{
    wchar_t szModule[MAX_PATH];
    GetModuleFileName(g_hInst, szModule, MAX_PATH);

    // 注册CLSID
    wchar_t szCLSID[64];
    StringFromGUID2(CLSID_PdfContextMenu, szCLSID, ARRAYSIZE(szCLSID));

    wchar_t szKey[256];
    HKEY hKey;

    // 创建CLSID键
    wsprintf(szKey, L"CLSID\\%s", szCLSID);
    if (RegCreateKey(HKEY_CLASSES_ROOT, szKey, &hKey) != ERROR_SUCCESS)
        return SELFREG_E_CLASS;
    RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)L"PDF Context Menu", sizeof(L"PDF Context Menu"));
    RegCloseKey(hKey);

    // 注册InprocServer32
    wsprintf(szKey, L"CLSID\\%s\\InprocServer32", szCLSID);
    if (RegCreateKey(HKEY_CLASSES_ROOT, szKey, &hKey) != ERROR_SUCCESS)
        return SELFREG_E_CLASS;
    RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szModule, (lstrlen(szModule) + 1) * sizeof(wchar_t));
    RegSetValueEx(hKey, L"ThreadingModel", 0, REG_SZ, (BYTE *)L"Apartment", sizeof(L"Apartment"));
    RegCloseKey(hKey);

    // 关联到所有文件类型
    if (RegCreateKey(HKEY_CLASSES_ROOT, L"*\\shellex\\ContextMenuHandlers\\PDFContextMenu", &hKey) != ERROR_SUCCESS)
        return SELFREG_E_CLASS;
    RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szCLSID, (lstrlen(szCLSID) + 1) * sizeof(wchar_t));
    RegCloseKey(hKey);

    return S_OK;
}

STDAPI DllUnregisterServer()
{
    wchar_t szCLSID[64];
    StringFromGUID2(CLSID_PdfContextMenu, szCLSID, ARRAYSIZE(szCLSID));

    wchar_t szKey[256];

    // 删除上下文菜单处理器注册
    wsprintf(szKey, L"*\\shellex\\ContextMenuHandlers\\PDFContextMenu");
    RegDeleteKey(HKEY_CLASSES_ROOT, szKey);

    // 删除CLSID键
    wsprintf(szKey, L"CLSID\\%s\\InprocServer32", szCLSID);
    RegDeleteKey(HKEY_CLASSES_ROOT, szKey);

    wsprintf(szKey, L"CLSID\\%s", szCLSID);
    RegDeleteKey(HKEY_CLASSES_ROOT, szKey);

    return S_OK;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        g_hInst = hModule;
        DisableThreadLibraryCalls(hModule);
        //获取当前目录
        char s_buffer[MAX_PATH] = {0};
        GetModuleFileNameA(hModule,s_buffer,MAX_PATH);        
        std::stringstream ss;
        PathRemoveFileSpecA(s_buffer);
        PathCombineA(s_buffer,s_buffer,"pdfprint.json");
        ss << "pdfdll@dllpath:" << s_buffer;
        OutputDebugStringA(ss.str().c_str());


    }
    break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}